package com.sitech.ibnms.c3p.task;

import com.sitech.ibnms.c3p.command.CommandParamsService;
import com.sitech.ibnms.c3p.command.IOParamType;
import com.sitech.ibnms.c3p.domain.*;
import com.sitech.ibnms.c3p.mappers.*;
import com.sitech.ibnms.c3p.task.listeners.BaselineWarningListener;
import com.sitech.ibnms.c3p.task.listeners.TaskCompleteListener;
import com.sitech.ibnms.c3p.task.listeners.TaskFailedListener;
import com.sitech.ibnms.c3p.util.C3pConstants;
import com.sitech.ibnms.c3p.util.JSONUtil;
import com.sitech.ibnms.c3p.util.TextDiffUtil;
import com.sitech.ismp.workflow.dao.ProcessInstanceDAO;
import com.sitech.ismp.workflow.domain.ProcessInstance;
import com.sitech.ismp.workflow.domain.Task;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.util.*;

/**
 * 智能巡检处理
 * Created by liujhc on 7/24.
 */
@Component
public class C3pIspResultProcessor implements TaskCompleteListener, TaskFailedListener {
    private Logger LOG = LoggerFactory.getLogger("FLUME");

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private C3pIspReportMapper ispReportMapper;
    @Autowired
    private C3pIspBaselineKpiMapper baselineKpiMapper;
    @Autowired
    private C3pTaskMapper c3pTaskMapper;
    @Autowired
    private C3pExecNoticeMapper execNoticeMapper;
    @Autowired
    private ProcessInstanceDAO processInstanceDAO;
    @Autowired
    private CommandParamsService paramsService;
    @Autowired
    @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
    private List<BaselineWarningListener> baselineWarningListeners;
    @Autowired
    private C3pServerLogTaskService logTaskService;
    @Autowired
    private VAssetAllMapper vAssetAllMapper;

    @Override
    public void onComplete(C3pTask c3pTask, ProcessInstance processInstance) throws Exception {
        int processId = processInstance.getProcessId();
        int serialNo = processInstance.getSerialNo();
        if (processInstance.getParentSerialNo() <= 0) { // 只有根任务才输出巡检报告
            logger.info("Process isp results: processId={}, serialNo={}", processId, serialNo);
            logTaskService.writeTaskLog(processId, serialNo, "【处理巡检结果】开始...");
            try {
                processIspResults(processId, serialNo);
            } catch (Exception e) {
                logTaskService.writeTaskLog(processId, serialNo, "  【错误】：" + e.getLocalizedMessage());
                throw e;
            }
            logTaskService.writeTaskLog(processId, serialNo, "【处理巡检结果】完成");
        } else {
            logger.info("Skip isp results processing : instance[processId={},serialNo={}] is created by [serialNo={},taskId={}]!",
                    processId, serialNo, processInstance.getParentSerialNo(), processInstance.getParentTaskId());
        }
    }

    @Override
    public void onFailed(C3pTask c3pTask, ProcessInstance processInstance, String failReason) throws Exception {
        onComplete(c3pTask, processInstance);
    }

    /**
     * 智能巡检结果处理
     *
     * @param processId
     * @param serialNo
     */
    private void processIspResults(int processId, int serialNo) {
        ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("javascript");
        String scheduleId = c3pTaskMapper.queryScheduleIdBySerialNo(serialNo);
        String c3pTaskId = c3pTaskMapper.queryC3pTaskIdByProcessId(processId);
        C3pTask c3pTask = c3pTaskMapper.findById(c3pTaskId);
        List<C3pIspBaselineKpi> baselineKpis = baselineKpiMapper.findByC3pTaskId(c3pTaskId);
        // 将list转为Map以备查询
        Map<String, C3pIspBaselineKpi> baselineKpiMap = transToMap(baselineKpis);
        // 任务关心的巡检指标
        boolean isInspectionTask = TaskConfType.inspectionItem.equals(c3pTask.getTaskConfType());
        Set<String> ispKPIs = isInspectionTask ? ispReportMapper.queryIspKPIs(c3pTaskId) : Collections.<String>emptySet();
        Collection<C3pTaskNodeIo> taskNodeIos = paramsService.queryParamValuesRecursively(serialNo, C3pConstants.CMD_OUT_KPIS, IOParamType.OUT);
        int issueRecords = 0; // 不正常记录数
        int allRecords = 0;
        List<String> issueKpiList = new ArrayList<String>();
        List<C3pIspReport> reports = new ArrayList<C3pIspReport>();
        for (C3pTaskNodeIo taskNodeIo : taskNodeIos) {
            String execC3pTaskId = taskNodeIo.getC3pTaskId();
            Task task = toTask(taskNodeIo);
            List<Map> kpis = JSONUtil.fromJSON(taskNodeIo.getParamValue(), new JSONUtil.TypeReference<List<Map>>() {
            });
            if (kpis == null || kpis.isEmpty()) {
                logger.warn("No kpis in results: {}", taskNodeIo.getParamValue());
                logTaskService.writeTaskNodeLog(task, "  环节【${nodeName}】" + taskNodeIo.getParamName().replace(C3pConstants.CMD_OUT_KPIS, "") + "没有巡检结果返回!");
                continue;
            }
            for (Map kpi : kpis) {
                String unitId = (String) kpi.get("UNIT_ID");
                String kpiId = (String) kpi.get("KPI_ID");
                String kpiValue =kpi.get("KPI_VALUE")==null?"":String.valueOf(kpi.get("KPI_VALUE"));
                String extUnitId = String.valueOf(kpi.get("EXT_UNIT_ID"));
                String position = kpi.containsKey("POSITION")?String.valueOf(kpi.get("POSITION")):"";
                int displayMode = kpi.containsKey("DISPLAY_MODE")?TextDiffUtil.returnInt(kpi.get("DISPLAY_MODE").toString()):0;
                String colName = displayMode==2?String.valueOf(kpi.get("COL_NAME")):unitId;
                if (isInspectionTask && !ispKPIs.contains(kpiId)) {
                    // 跳过此记录：不是巡检任务关心的指标
                    logger.info("Skip UNIT_ID:{}, KPI_ID:{}, KPI_VALUE:{}, C3P_TASK_ID:{}, EXEC_ID:{} ,EXT_UNIT_ID:{} ,POSITION:{} for C3P_TASK_ITEM.KPI_ID",
                            unitId, kpiId, kpiValue, c3pTaskId, serialNo,extUnitId,position);
                    continue;
                }
                C3pIspBaselineKpi baselineKpi = baselineKpiMap.get(kpiId);
                logger.info("Insert UNIT_ID:{}, KPI_ID:{}, KPI_VALUE:{}, baselineKpi:{}, extUnitId:{}, position:{}", unitId, kpiId, kpiValue, baselineKpi,extUnitId,position);
                C3pIspReport ispReport = new C3pIspReport();
                if (baselineKpi != null) {
                    String express = baselineKpi.getExpress();
                    scriptEngine.put("KPI_VALUE", kpiValue);
                    try {
                        Object result = scriptEngine.eval(express);
                        //1:正常，0错误，-1异常
                        ispReport.setCompareRst(Boolean.TRUE.equals(result) ? 0 : 1);
                    } catch (Exception e) {
                        ispReport.setCompareRst(-1);
                        ispReport.setCompareDesc("COMPARE ERROR");
                        logger.warn(String.format("inspection expression[%s] execute error: serialNo=%s, KPI_VALUE=%s",
                                express, serialNo, JSONUtil.toJSON(kpiValue)), e);
                        logTaskService.writeTaskNodeLog(task,
                                String.format("  【警告】基线表达式[%s] 执行错误(KPI_VALUE=%s): %s",
                                        express, JSONUtil.toJSON(kpiValue), e.getLocalizedMessage()));
                    }
                    ispReport.setBaseExpression(express);
                    ispReport.setBaseValue(baselineKpi.getBaseLineRegion());
                } else {
                    ispReport.setCompareRst(1);
                    ispReport.setCompareDesc("NO BASELINE");
                }
                ispReport.setTaskId(c3pTaskId);
                ispReport.setActivityId(taskNodeIo.getActivityId());
                if (!c3pTaskId.equals(execC3pTaskId)) {
                    ispReport.setSubTaskId(execC3pTaskId);
                }
                ispReport.setScheduleId(StringUtils.defaultString(scheduleId, "N/A"));
                ispReport.setUnitId(unitId);
                ispReport.setKpiId(kpiId);
                ispReport.setKpiValue(kpiValue);
                ispReport.setCreateTime(new Date());
                ispReport.setExecId(String.valueOf(serialNo));
                ispReport.setExtUnitId(extUnitId);
                ispReport.setPosition(position);
                ispReport.setDisplayMode(displayMode);
                ispReport.setColName(colName);
                writeFlumeLog(ispReport);

                reports.add(ispReport);
                // 只有巡检比较结果为0才表示正常
                if (ispReport.getCompareRst() != 0) {
                    issueRecords += 1;
                    issueKpiList.add(kpiId);
                }
                allRecords += 1;
            }
            logger.info("insert {} isp reports for task[{}]", reports.size(), taskNodeIo.getTaskId());
            // 每个环节进行一次巡检结果入库
            flushReports(reports);
        }

        logTaskService.writeTaskLog(processId, serialNo, String.format("  巡检的基线对比结果为: %s/%s(正常/总数)",
                (allRecords - issueRecords), allRecords));
        if (issueRecords > 0) {
            processBaselineWarning(c3pTask, serialNo, scheduleId, issueKpiList, allRecords, issueRecords);
        }
    }

    private Task toTask(C3pTaskNodeIo taskNodeIo) {
        Task task = new Task();
        task.setSerialNo(taskNodeIo.getSerialNo());
        task.setTaskId(taskNodeIo.getTaskId());
        task.setCurrActId(taskNodeIo.getActivityId());
        task.setProcessId(taskNodeIo.getProcessId());
        return task;
    }

    private void flushReports(List<C3pIspReport> reports) {
        if (!reports.isEmpty()) {
            ispReportMapper.insert(reports);
            reports.clear();
        }
    }

    private Map<String, C3pIspBaselineKpi> transToMap(List<C3pIspBaselineKpi> baselineKpis) {
        Map<String, C3pIspBaselineKpi> baselineKpiMap = new HashMap<String, C3pIspBaselineKpi>(baselineKpis.size());
        for (C3pIspBaselineKpi baselineKpi : baselineKpis) {
            baselineKpiMap.put(baselineKpi.getKpiId(), baselineKpi);
        }
        return baselineKpiMap;
    }

    private void processBaselineWarning(C3pTask c3pTask, int serialNo, String scheduleId, List<String> issueKpiList, int allRecords, int issueRecords) {
        C3pExecNotice execNotice = new C3pExecNotice();
        execNotice.setTaskId(c3pTask.getTaskId());
        execNotice.setExecId(String.valueOf(serialNo));
        execNotice.setNoticeType("baseline");
        execNotice.setScheduleId(scheduleId);
        ProcessInstance processInstance = processInstanceDAO.queryProcessInstanceInfoBySerialNo(serialNo);

        // 在短信内容中翻译巡检指标
        int translateNum = 2;
        int length = issueKpiList.size() > translateNum ? translateNum : issueKpiList.size();
        StringBuilder kpiNames = new StringBuilder();
        for (int i = 0; i < length; i++) {
            kpiNames.append(",“").append(ispReportMapper.getKpiNameByKpiId(issueKpiList.get(i))).append("”");
        }

        execNotice.setNoticeContent(String.format("任务【%s】共%d个巡检项结果，其中有%s等%d个与基线值比较有异常，任务启动时间为%s",
                c3pTask.getTaskName(), allRecords, kpiNames.substring(1), issueRecords, DateFormatUtils.format(processInstance.getCreateTime(), "yyyy年MM月dd日HH:mm:ss")));
        execNotice.setExecBeginTime(processInstance.getCreateTime());
        if (processInstance.getCompleteTime() != null) {
            execNotice.setExecuteTime((int) (processInstance.getCompleteTime().getTime() - processInstance.getCreateTime().getTime()));
        } else {
            execNotice.setExecuteTime(-1);
        }
        execNoticeMapper.insert(execNotice);

        for (BaselineWarningListener taskListener : baselineWarningListeners) {
            try {
                taskListener.onBaselineWarning(c3pTask, processInstance, execNotice);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

    private void writeFlumeLog(C3pIspReport ispReport) {
        try {
            Map<String, String> logMap = new HashMap<String, String>();

            logMap.put("ciid", vAssetAllMapper.getCiCodeByUnitId(getExtUnitId(ispReport.getUnitId())));
            logMap.put("specId", "");
            //logMap.put("productId", productId[random.nextInt(2)]);
            //logMap.put("bizType", bizType[random.nextInt(3)]);
            logMap.put("linkType", "C3P");
            //logMap.put("serverIp", "172.21.2.96");
            //logMap.put("serverPort", "");
            //logMap.put("className", "com.sitech.test.Main");
            //logMap.put("methodName", "excute");
            logMap.put("level", ispReport.getCompareRst() != 0 ? "error" : "info");
            logMap.put("timestamp", String.valueOf(System.currentTimeMillis()));
            logMap.put("content", JSONUtil.toJSON(ispReport));

            LOG.info(JSONUtil.toJSON(logMap));
        } catch (Exception e) {
            logger.error("error when write flume log", e);
        }


    }

    private String getExtUnitId(String unitId) {
        String extUnitId = "";
        if (unitId.startsWith("10")) {
            String[] elem = unitId.split(":");
            if (elem.length >= 2) {
                String[] kbp = elem[0].split("-");
                if (kbp.length >= 3) {
                    extUnitId = kbp[0] + "-" + kbp[1] + "-" + kbp[2];
                } else {
                    extUnitId = elem[0];
                }

                String deviceId = elem[1].split("-")[0];

                extUnitId += ":" + deviceId;
            } else {
                extUnitId = elem[0];
            }
        } else {
            String[] elem = unitId.split(":");
            extUnitId = elem[0];
        }
        return extUnitId;
    }
}
